﻿using HMaker.Events;
using HMaker.Utils;
using System.Drawing;
using System.Windows.Forms;

namespace HMaker.Controls
{
    /// <summary>
    /// 图片编辑控件
    /// </summary>
    public class PicEditor : Panel
    {
        #region 枚举
        public enum ToolType
        {
            Brush,      //用于覆盖一种颜色
            Pencil,     //铅笔,用于写入单个像素
            Pipette,    //吸管,用于获取当前点击的颜色
            MagicWand,  //魔术棒模式
            Clip,       //裁剪模式
            Locate,     //定位模式
            None
        }
        #endregion

        #region 内部数据
        private Bitmap _Image;

        /// <summary>
        /// 承载图片的控件
        /// </summary>
        private readonly PictureBox _PictureBox;

        /// <summary>
        /// 当前选择的编辑工具
        /// </summary>
        private ToolType _CurToolType = ToolType.Clip;

        /// <summary>
        /// 显示图片时的缩放系数
        /// </summary>
        private int _Scalar = 4;

        /// <summary>
        /// 当前选择的颜色
        /// </summary>
        private Color _SelectColor = Color.Black;

        /// <summary>
        /// 魔术棒工具的相似度
        /// </summary>
        private double _Semblance = 0;

        /// <summary>
        /// 魔术棒的选择方式(正选、反选)
        /// </summary>
        private bool _InverseSelect = false;

        //还需要一个历史记录类

        #endregion

        #region 对外接口
        public event SelectedAreaChangedEventHandler OnSelectedChanged;
        public event SelectedColorChangedEventHandler OnSelectedColorChanged;
        public event ScalarChangedEventHandler OnScalarChangedEventHandler;
        public event SelectedPointChangedEventHandler OnSelectedPointChanged;

        /// <summary>
        /// 当前正在进行编辑的图片
        /// </summary>
        public Bitmap Image
        {
            get => _Image;
            set { _Image = value; UpdateImage(); }
        }

        /// <summary>
        /// 当前选择的工具
        /// </summary>
        public ToolType CurToolType
        {
            get => _CurToolType;
            set
            {
                if (_CurToolType != value)
                {
                    _CurToolType = value;
                    UpdateCursor();
                    UpdateImage();
                }
            }
        }

        /// <summary>
        /// 设置或读取缩放系数
        /// </summary>
        public int Scalar
        {
            get => _Scalar;
            set
            {
                if (value >= 1 && value <= 16)
                {
                    if (_Scalar != value)
                    {
                        _Scalar = value;
                        _IsDisplayClipRect = false;
                        UpdateImage();
                        OnScalarChangedEventHandler?.Invoke(this, new ScalarChangedEventArgs(_Scalar));
                    }
                }
            }
        }

        /// <summary>
        /// 设置或读取当前选择的颜色
        /// </summary>
        public Color SelectColor
        {
            get => _SelectColor;
            set
            {
                _SelectColor = value;
                OnSelectedColorChanged?.Invoke(this, new SelectedColorChangedEventArgs() { Color = value });
            }
        }

        /// <summary>
        /// 读取或设置魔术棒工具的相似度
        /// </summary>
        public double Semblance
        {
            get => _Semblance;
            set
            {
                if (_Semblance >= 0 && _Semblance <= 100)
                {
                    _Semblance = value / 100;
                    if (CurToolType == ToolType.MagicWand && _IsDisplayMagicWandPreview)
                    {
                        PreviewMagicWand();
                    }
                }
            }
        }

        /// <summary>
        /// 读取设置魔术棒的选择方式(正选、反选)
        /// </summary>
        public bool InverseSelect
        {
            get => _InverseSelect;
            set
            {
                _InverseSelect = value;
                if (CurToolType == ToolType.MagicWand && _IsDisplayMagicWandPreview)
                {
                    PreviewMagicWand();
                }
            }
        }

        /// <summary>
        /// 将图片裁剪为选定的部分
        /// </summary>
        public void ClipIt()
        {
            if (CurToolType == ToolType.Clip && _IsDisplayClipRect)
            {
                Rectangle rect = _ClipRectangle;
                rect.X /= _Scalar;
                rect.Y /= _Scalar;
                rect.Width /= _Scalar;
                rect.Height /= _Scalar;
                _Image = BitmapUtils.Clip(_Image, rect);
                _IsDisplayClipRect = false;
                UpdateImage();
            }
        }

        /// <summary>
        /// 将图片边框多余的部分裁减掉
        /// </summary>
        public void AutoClipIt()
        {
            Bitmap tmp = BitmapUtils.AutoClip(_Image);
            if (tmp != null)
            {
                _Image = tmp;
                UpdateImage();
            }
        }

        /// <summary>
        /// 应用魔术棒的选择
        /// </summary>
        public void ConfirmMagicWand()
        {
            if (_IsDisplayMagicWandPreview)
            {
                Color color = _Image.GetPixel(_MagicWandDownPos.X, _MagicWandDownPos.Y);
                Image = BitmapUtils.ReplaceColor(_Image, color, Semblance, SelectColor, !InverseSelect);
                _IsDisplayMagicWandPreview = false;
            }
        }

        /// <summary>
        /// 随机获取一种颜色,使得这个颜色和图片不会发生冲突
        /// </summary>
        /// <param name="deleta"></param>
        /// <param name="result"></param>
        /// <returns></returns>
        public bool FindCollisionlessColor(double similarly)
        {
            if (BitmapUtils.FindCollisionlessColor(_Image, similarly, out Color result))
            {
                SelectColor = result;
                return true;
            }
            else
            {
                return false;
            }
        }


        public PicEditor()
        {
            AutoScroll = true;
            _PictureBox = new PictureBox()
            {
                SizeMode = PictureBoxSizeMode.AutoSize,
                Cursor = Cursors.Default,
                BorderStyle = BorderStyle.FixedSingle,
                Size = Size.Empty
            };

            _PictureBox.MouseDown += PictureBox_MouseDown;
            _PictureBox.MouseUp += PictureBox_MouseUp;
            _PictureBox.MouseMove += PictureBox_MouseMove;
            _PictureBox.Paint += PictureBox_Paint;
            Controls.Add(_PictureBox);
        }

        #endregion
        #region 控件行为和绘制

        /// <summary>
        /// 裁剪框绘制画笔
        /// </summary>
        private readonly Pen ClipRectanglePen = new Pen(Color.Black);

        /// <summary>
        /// 裁剪开始点(计算缩放后的数据)
        /// </summary>
        private Point _MouseDownPos = Point.Empty;

        /// <summary>
        /// 魔术棒上次的点击的位置
        /// </summary>
        private Point _MagicWandDownPos = Point.Empty;

        /// <summary>
        /// 裁剪的画图区域(计算缩放后的数据)
        /// </summary>
        private Rectangle _ClipRectangle = Rectangle.Empty;

        /// <summary>
        /// 是否显示裁剪框
        /// </summary>
        private bool _IsDisplayClipRect = false;

        /// <summary>
        /// 是否需要显示魔术棒的预览
        /// </summary>
        private bool _IsDisplayMagicWandPreview = false;

        /// <summary>
        /// 表示当前正在拖拽
        /// </summary>
        private bool _IsDrag = false;

        /// <summary>
        /// 将图片更新到显示框中,若指定了srcBitmap,则不采用设定的图片
        /// </summary>
        private void UpdateImage(Bitmap srcBitmap = null)
        {
            if (_Image == null && srcBitmap == null)
            {
                return;
            }
            _PictureBox.Image?.Dispose();
            _PictureBox.Image = BitmapUtils.Enlarge(srcBitmap ?? _Image, _Scalar);
        }

        /// <summary>
        /// 预览魔术棒的选择
        /// </summary>
        private void PreviewMagicWand()
        {
            Color color = _Image.GetPixel(_MagicWandDownPos.X, _MagicWandDownPos.Y);
            CoverImage(BitmapUtils.FindColors(_Image, color, Semblance, Color.FromArgb(160, color.GetInverseColor()), !InverseSelect));
        }

        /// <summary>
        /// 预览位置选择
        /// </summary>
        private void PreviewLocation()
        {
            Color color = _Image.GetPixel(_MouseDownPos.X, _MouseDownPos.Y);
            CoverImage(BitmapUtils.ReplaceColor(_Image, _MouseDownPos, color.GetInverseColor()));
        }



        /// <summary>
        /// 在原背景图的基础上,将bitmap覆盖上去(此函数不改变控件中的图片,在下次UpdateImage()时会被清除效果)
        /// </summary>
        /// <param name="bitmap"></param>
        private void CoverImage(Bitmap bitmap)
        {
            Bitmap dstbitmap = (Bitmap)_Image.Clone();
            using (Graphics g = Graphics.FromImage(dstbitmap))
            {
                g.DrawImageUnscaled(bitmap, Point.Empty);
            }
            UpdateImage(dstbitmap);
        }

        /// <summary>
        /// 用于更新光标
        /// </summary>
        private void UpdateCursor()
        {
            switch (_CurToolType)
            {
                case ToolType.Clip:
                    _PictureBox.Cursor = Cursors.Cross;
                    break;
                default:
                    _PictureBox.Cursor = Cursors.Arrow;
                    break;
            }
        }


        /// <summary>
        /// 对齐点到网格单元格中心(用于放大后的图像)
        /// </summary>
        /// <param name="location"></param>
        /// <returns></returns>
        private Point AlignPointToGrid(Point location)
        {
            int scalar = _Scalar / 2;
            return new Point(location.X - location.X % _Scalar + scalar, location.Y - location.Y % _Scalar + scalar);
        }

        /// <summary>
        /// 获取当前已经被选择的区域
        /// </summary>
        /// <param name="targetPoint"></param>
        /// <returns></returns>
        private Rectangle GetSelectedRectangle(Point targetPoint)
        {
            return Util.GetRectangleByTwoPoint(_MouseDownPos, targetPoint);
        }

        private void PictureBox_Paint(object sender, PaintEventArgs e)
        {
            if (_IsDisplayClipRect && _CurToolType == ToolType.Clip)
            {
                e.Graphics.PageUnit = GraphicsUnit.Pixel;
                ClipRectanglePen.Width = Scalar;
                e.Graphics.DrawRectangle(ClipRectanglePen, _ClipRectangle);
            }
        }
        protected override void OnMouseDown(MouseEventArgs e)
        {
            base.OnMouseDown(e);
            Focus();
        }

        private void PictureBox_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                _IsDisplayClipRect = false;
                _IsDisplayMagicWandPreview = false;
                Point p = BitmapUtils.PointToOrgin(e.Location, _Scalar);
                switch (CurToolType)
                {
                    case ToolType.Pencil:
                        {
                            _Image.SetPixel(p.X, p.Y, _SelectColor);
                            UpdateImage();
                        }
                        break;
                    case ToolType.Brush:
                        {
                            Color color = _Image.GetPixel(p.X, p.Y);
                            _Image = BitmapUtils.ReplaceColor(_Image, color, SelectColor);
                            UpdateImage();
                        }
                        break;
                    case ToolType.Clip:
                        {
                            _IsDrag = true;
                            Color color = _Image.GetPixel(p.X, p.Y).GetInverseColor();
                            ClipRectanglePen.Color = Color.FromArgb(180, color);
                            _MouseDownPos = AlignPointToGrid(e.Location);
                            _IsDisplayClipRect = true;
                        }
                        break;
                    case ToolType.Pipette:
                        {
                            SelectColor = _Image.GetPixel(p.X, p.Y);
                        }
                        break;
                    case ToolType.MagicWand:
                        {
                            _IsDisplayMagicWandPreview = true;
                            _MagicWandDownPos = p;
                            PreviewMagicWand();
                        }
                        break;
                    case ToolType.Locate:
                        {
                            OnSelectedPointChanged?.Invoke(this, new SelectedPointChangedEventArgs(p));
                            _MouseDownPos = p;
                            PreviewLocation();
                        }
                        break;
                }
            }
            Focus();
        }



        private void PictureBox_MouseMove(object sender, MouseEventArgs e)
        {
            if (_IsDrag)
            {
                if (CurToolType == ToolType.Clip)
                {
                    _ClipRectangle = GetSelectedRectangle(AlignPointToGrid(e.Location));
                    _PictureBox.Invalidate();
                }
            }
        }

        private void PictureBox_MouseUp(object sender, MouseEventArgs e)
        {
            _IsDrag = false;
            Rectangle rect = Util.GetRectangleByTwoPoint(BitmapUtils.PointToOrgin(_MouseDownPos, _Scalar), BitmapUtils.PointToOrgin(e.Location, _Scalar));
            OnSelectedChanged?.Invoke(this, new SelectedAreaChangedEventArgs()
            {
                Area = rect,
                Bitmap = BitmapUtils.Clip(_Image, rect)
            });
        }

        protected override void OnMouseWheel(MouseEventArgs e)
        {
            base.OnMouseWheel(e);
            if (Keys.Control == ModifierKeys)
            {
                if (e.Delta > 0)
                {
                    Scalar++;
                }
                else if (e.Delta < 0)
                {
                    Scalar--;
                }
            }
        }

        #endregion
    }
}
